package ru.picman;

import java.util.ArrayList;
import java.util.List;

import ru.picman.ui.WorldPainter;

import android.graphics.Point;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

/**
 * The World is the class that holds all the data of - among
 * others - all the units on the screen, the levels and the
 * power-ups.
 * 
 * @author Matthijs
 */
public class World
{
	/**
	 * Constructs the world. This world has no level or units
	 * loaded yet. All the constructor does is initializing the
	 * arrays and such.
	 * 
	 * @param game The game this world belongs to
	 */
	public World(PicmanGame game) {
		this.game = game;
		this.entities = new ArrayList<Entity>();
		this.painter = new WorldPainter(this);
		game.getView().addPaintable(painter);
		
		reset();
	}
	
	/**
	 * Ticks the world.
	 */
	public void tick() {
		for (int i = 0; i < grid.length; ++i) {
			for (int j = 0; j < grid[i].length; ++j) {
				if (grid[i][j] != null && grid[i][j].isDestroyed())
					grid[i][j] = null;
				if (eggs[i][j] != null && eggs[i][j].isTaken())
					eggs[i][j] = null;
			}
		}
		
		TextView scoreView = null; 
		scoreView = (TextView)game.findViewById(R.id.score_value_text); 
		scoreView.setText(String.valueOf(player.getScore()));
		
		ImageView imageView = null;
		int lives = player.getLives();
		if (lives == 2)
			imageView = (ImageView)game.findViewById(R.id.lives_live3_view);
		else if (lives == 1)
			imageView = (ImageView)game.findViewById(R.id.lives_live2_view);
		else if (lives == 0)
			imageView = (ImageView)game.findViewById(R.id.lives_live1_view);
		if (imageView != null)
			imageView.setVisibility(View.INVISIBLE);
		
		
			
	}
	
	/**
	 * Loads the specified level. After loadLevel is called, the
	 * the world will be drawn with that level. The string array
	 * is typically loaded by the Android tools, from an XML file.<br />
	 * <br />
	 * For more info on how these files are supposed to be built
	 * up, see file <i>/res/values/levels.xml</i>
	 * 
	 * @param level The string array representation of the level to load
	 */
	public void loadLevel(String[] level) {
		if (player == null)
			throw new NullPointerException("No player added to the world");
		boolean apSpecial = false;
		int y = 0;
		int pigCount = 0;
		int maxPigCount = (PicmanOptions.difficulty.equals("Easy") ? 2 : (PicmanOptions.difficulty.equals("Medium") ? 4 : Integer.MAX_VALUE ));
		for (String s : level) {
			s = s.trim();
			if (s.startsWith("#") || s.length() == 0)
				continue;
			if (s.contains(",")) {
				String[] split = s.split(",");
				w = Integer.parseInt(split[0]);
				h = Integer.parseInt(split[1]);
				apSpecial = (split[2].equals("T") || split[2].equals("t") ? true : false);
				reset();
				continue;
			}
			for (int x = 0; x < w; ++x) {
				char c = s.charAt(x);
				try {
					int blockType = Integer.parseInt(String.valueOf(c));
					grid[x][y] = new Block(blockType);
					if (blockType == Block.BLOCK_WOOD && apSpecial && PicmanGame.RAND.nextBoolean())
						eggs[x][y] = createEgg(Egg.EGG_GOLD,x,y);
				}
				catch (NumberFormatException nfe) {
					if ((c == 'B' || c == 'b') && player != null) {
						player.getUnit().setPosition(x,y);
					}
					if (c == 'P') {
						if (++pigCount > maxPigCount)
							continue;
						Pig pig = new Pig();
						pig.setPosition(x,y);
						addEntity(pig);
					}
					if (c == 'S')
						eggs[x][y] = createEgg(Egg.EGG_GOLD,x,y);
					else if (c != 'F')
						eggs[x][y] = createEgg(apSpecial && PicmanGame.RAND.nextInt(100) < 3 ? Egg.EGG_GOLD : Egg.EGG_WHITE,x,y);
				}
			}
			++y;
		}
	}
	
	/**
	 * Resets the world.
	 */
	public void reset() {
		grid = new Block[w][h];
		eggs = new Egg[w][h];
//		for (Entity e : getEntities())
//			if (e.getType() == EntityType.PIG)
//				removeEntity(e);
	}
	
	/**
	 * Used to indicate that a particular tile (set with loadTile and referenced
	 * by an integer) should be drawn at the given x/y coordinates during the
	 * next invalidate/draw cycle.
	 * 
	 * @param x The tile X
	 * @param y The tile y
	 * @param type The block type. A negative value is seen as <i>none</i>
	 */
	public void setTile(int x, int y, int type) {
		grid[x][y] = (type < 0 ? null : new Block(type));
	}
	
	/**
	 * Checks if there is no block at the specified point.<br />
	 * <b>Note:</b> A tile with units only on it is seen as free.
	 * 
	 * @param p The tile at this point
	 * @return true if there's no block on the tile
	 */
	public boolean isFree(Point p) {
		return grid[p.x][p.y] == null;
	}
	
	/**
	 * Checks if the specified coordinates are valid, i.e. inside
	 * this world.
	 * 
	 * @param x The x
	 * @param y The y
	 * @return true if (x,y) is a valid location in this world
	 */
	public boolean isValid(int x, int y) {
		return x >= 0 && y >= 0 && x < w && y < h;
	}
	
	/**
	 * Adds an entity to the world. The location of the entity
	 * can be changed by methods of the entity itself.
	 * 
	 * @param e The entity
	 */
	public void addEntity(Entity e) {
		entities.add(e);
		e.setWorld(this);
		e.startAIThread();
	}
	
	/**
	 * Removes an entity from the world.
	 * 
	 * @param e The entity to remove
	 */
	public void removeEntity(Entity e) {
		entities.remove(e);
		e.stopAIThread();
	}
	
	/**
	 * Returns all entities at the specified point.
	 * 
	 * @param p The point
	 * @return All entities of which the rounded position equals <i>p</i>
	 */
	public Entity[] getEntitiesAt(Point p) {
		List<Entity> ets = new ArrayList<Entity>();
		for (Entity e : entities)
			if (e.getPositionI().equals(p))
				ets.add(e);
		return ets.toArray(new Entity[ets.size()]);
	}
	
	/**
	 * Returns all entities in the world.
	 * 
	 * @return All entities
	 */
	public Entity[] getEntities() {
		return entities.toArray(new Entity[entities.size()]);
	}
	
	/**
	 * Sets the player in this world. A world can only have one player.
	 * 
	 * @param player The player
	 */
	public void setPlayer(Player player) {
		this.player = player;
	}
	
	/**
	 * Returns the set player.
	 * 
	 * @return The player
	 */
	public Player getPlayer() {
		return player;
	}
	
	/**
	 * Returns the game this world belongs to.
	 * 
	 * @return The game
	 */
	public PicmanGame getGame() {
		return game;
	}
	
	/**
	 * Returns the width of the world. The width is the same
	 * as the amount of tiles in one horizontal line.
	 * 
	 * @return The width, or -1 if no level has been loaded
	 */
	public int getWidth() {
		return w;
	}
	
	/**
	 * Returns the height of the world. The height is the same
	 * as the amount of tiles in one vertical line.
	 * 
	 * @return The height, or -1 if no level has been loaded
	 */
	public int getHeight() {
		return h;
	}
	
	/**
	 * Returns the grid of the world.
	 * 
	 * @return The grid
	 */
	public Block[][] getGrid() {
		return grid;
	}
	
	/**
	 * Returns the block at the specified location. This returns 
	 * null if there's no block at that position.
	 * 
	 * @param x The x position of the block
	 * @param y The y position of the block
	 * @return The block
	 */
	public Block getBlock(int x, int y) {
		return grid[x][y];
	}
	
	/**
	 * Returns the eggs in the world.
	 * 
	 * @return The eggs
	 */
	public Egg[][] getEggs() {
		return eggs;
	}
	
	/**
	 * Returns the egg at the specified location. This returns
	 * null if there's no egg at that position.
	 * 
	 * @param x The x position of the egg
	 * @param y The y position of the egg
	 * @return The egg
	 */
	public Egg getEgg(int x, int y) {
		return eggs[x][y];
	}
	
	/**
	 * Returns the current tile size.
	 * 
	 * @return The size
	 * @see WorldPainter#getTileSize()
	 */
	public int getTileSize() {
		return painter.getTileSize();
	}
	
	//
	
	/**
	 * Creates an egg and sets it's data.
	 * 
	 * @param type The type of the egg
	 * @param x The x location of the egg
	 * @param y The y location of the egg
	 * @return The egg
	 */
	private Egg createEgg(int type, int x, int y) {
		Egg egg = new Egg(type,this);
		egg.setPosition(x,y);
		return egg;
	}
	
	//
	
	private Player             player;
	private Block[][]          grid;
	private Egg[][]			   eggs;
	private int                w, h;
	
	private final List<Entity> entities;
	private final PicmanGame       game;
	private final WorldPainter painter;
	
}